其他
腾讯今年的校招薪资。。。。
The following article is from 吴师兄学算法 Author 吴师兄
第一时间收到文章更新
在 2024 年,腾讯的校园招聘再次证明了其作为中国互联网行业巨头的地位。
以下是对腾讯在2024年校园招聘期间针对程序员职位的薪酬待遇的详细分析(数据来源于网络):
普通Offer:普通Offer的薪酬范围为月薪19k-22k,年度总计薪酬在35w至43w之间。这个数字是基于月薪乘以16(考虑年终奖),再加上每月4k的房补和3w的签字费(仅部分岗位提供)计算得出。 SP Offer:对于更高级的职位,即SP Offer,月薪在23k-24k之间,年薪在44w至49w之间。这个数字包括月薪乘以16,每月4k的房补,3w的签字费,以及6w/2年的股票。 SSP Offer:SSP Offer提供最高的薪酬,月薪在26k-29k,年薪在50w至61w之间。这包括月薪乘以16,每月4k房补,5w签字费和6w-10w/2年的股票。 腾讯技术大咖计划:此外,腾讯还推出了针对卓越博士生的“腾讯技术大咖”招聘计划。此计划的总包Offer可达100w+,展示了腾讯对顶尖技术人才的高度重视。
科普之后,继续学习吧,腾讯的高频题奉上一道。
题目描述如下:
给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表。
k 是一个正整数,它的值小于或等于链表的长度。
如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。
这个问题要求我们将链表中的每k个节点作为一组进行翻转,如果链表中的节点数不是k的整数倍,那么最后剩余的节点保持原有顺序。思路如下:
设置虚拟节点:
首先,我们创建一个虚拟节点 dummy
,并将其next
指向链表的头节点head
。这个虚拟节点是为了方便处理边界情况,如翻转链表的首部。
pre
指针指向虚拟节点,它将用于标记每k个节点组的开始之前的节点。end
指针也初始化为虚拟节点,它将用于遍历找到每k个节点组的末尾。
使用一个 while
循环来遍历链表。在循环内部,我们首先使用一个for
循环来定位当前k个节点组的末尾 (end
指针)。如果 end
为null
,意味着当前组的节点数少于k,我们就保持这部分链表不变,退出循环。
一旦找到k个节点组,我们将该组从链表中断开。 next
指针保存了当前组之后部分的链表头。调用 reverse
函数来翻转这k个节点组。start
指针指向该组的头节点。
翻转后,我们重新连接链表。 pre.next
指向翻转后的头节点(原来的end
),而翻转后的尾节点(原来的start
)连接到next
。
更新 pre
和end
指针,准备下一组k个节点的翻转。
最终返回 dummy.next
,这是翻转后的链表的头节点。
reverse
:这是一个递归函数,用于翻转链表的一部分。它递归地翻转直到到达链表尾部,然后逐层返回并更新每个节点的 next
指针,以实现翻转。
通过这种方法,我们可以有效地每k个节点为一组进行翻转,同时保持链表的其余部分不变。
这个问题的关键是对链表的操作技巧,包括使用虚拟节点处理边界情况,以及递归翻转链表的一部分。
递归函数的理解和链表的指针操作是这个问题的难点,但一旦掌握,便能有效解决类似的链表操作问题。
class Solution {
public ListNode reverseKGroup(ListNode head, int k) {
// 一开始设置一个虚拟节点,它的值为 -1,它的值可以设置为任何的数,因为我们根本不需要使用它的值
ListNode dummy = new ListNode(-1);
// 虚拟头节点的下一节点指向 head 节点
// 如果原链表是 1 --> 2 --> 3
// 那么加上虚拟头节点就是 -1 --> 1 --> 2 --> 3
dummy.next = head;
// 设置一个指针,指向此时的虚拟节点,pre 表示每次要翻转的链表的头结点的【上一个节点】
// pre: -1 --> 1 --> 2 --> 3
ListNode pre = dummy;
// 设置一个指针,指向此时的虚拟节点,end 表示每次要翻转的链表的尾节点
// end: -1 --> 1 --> 2 --> 3
ListNode end = dummy;
// 通过 while 循环,不断的找到翻转链表的尾部
while( end.next != null ){
// 通过 for 循环,找到【每一组翻转链表的尾部】
// 由于原链表按照 k 个一组进行划分会可能出现有一组的长度不足 k 个
// 比如原链表 1 --> 2 --> 3 --> 4 --> 5
// k = 2,划分了三组 1 --> 2, 3 --> 4, 5
// 所以得确保 end 不为空才去找它的 next 指针,否则 null.next 会报错
for(int i = 0 ; i < k && end != null ; i++){
// end 不断的向后移动,移动 k 次到达【每一组翻转链表的尾部】
end = end.next;
}
// 如果发现 end == null,说明此时翻转的链表的节点数小于 k ,保存原有顺序就行
if(end == null){
// 直接跳出循环,只执行下面的翻转操作
break;
}
// next 表示【待翻转链表区域】里面的第一个节点
ListNode next = end.next;
// 【翻转链表区域】的最尾部节点先断开
end.next = null;
// start 表示【翻转链表区域】里面的第一个节点
ListNode start = pre.next;
// 【翻转链表区域】的最头部节点和前面断开
pre.next = null;
// 这个时候,【翻转链表区域】的头节点是 start,尾节点是 end
// 开始执行【反转链表】操作
// 原先是 start --> ...--> end
// 现在变成了 end --> ...--> start
// 要翻转的链表的头结点的【上一个节点】的 next 指针指向这次翻转的结果
pre.next = reverse(start);
// 接下来的操作是在为【待翻转链表区域】的反转做准备
// 原先是 start --> ...--> end
// 现在变成了 end --> ...--> start
// 【翻转链表区域】里面的尾节点的 next 指针指向【待翻转链表区域】里面的第一个节点
start.next = next;
// 原先是 start --> ...--> end
// 现在变成了 end --> ...--> start
// pre 表示每次要翻转的链表的头结点的【上一个节点】
pre = start;
// 将 end 重置为【待翻转链表区域】的头结点的上一个节点。
end = start;
}
return dummy.next;
}
// 反转链表的代码
private ListNode reverse(ListNode head) {
// 寻找递归终止条件
// 1、head 指向的结点为 null
// 2、head 指向的结点的下一个结点为 null
// 在这两种情况下,反转之后的结果还是它自己本身
if( head == null || head.next == null) return head;
// 不断的通过递归调用,直到无法递归下去,递归的最小粒度是在最后一个节点
// 因为到最后一个节点的时候,由于当前节点 head 的 next 节点是空,所以会直接返回 head
ListNode cur = reverse(head.next);
// 比如原链表为 1 --> 2 --> 3 --> 4 --> 5
// 第一次执行下面代码的时候,head 为 4,那么 head.next = 5
// 那么 head.next.next 就是 5.next ,意思就是去设置 5 的下一个节点
// 等号右侧为 head,意思就是设置 5 的下一个节点是 4
// 这里出现了两个 next
// 第一个 next 是「获取」 head 的下一节点
// 第二个 next 是「设置」 当前节点的下一节点为等号右侧的值
head.next.next = head;
// head 原来的下一节点指向自己,所以 head 自己本身就不能再指向原来的下一节点了
// 否则会发生无限循环
head.next = null;
// 我们把每次反转后的结果传递给上一层
return cur;
}
}
大厂程序员提倡“防御性编程”:故意把代码写得很烂,万一自己被裁,要确保留下的代码不可维护! 雷军三十年前计算机论文曝光:“这种智能判定方法,国内外文献尚未提及” 有这个迹象,公司就要凉了 工作群,“逼疯”打工人 计算机科学考古:冯·诺依曼的第一个计算机程序